/**
 * event 实现
 * Author: bosscheng
 * Data: 14-12-10
 */


// Events
// -----------------
// Thanks to:
//  - https://github.com/documentcloud/backbone/blob/master/backbone.js
//  - https://github.com/joyent/node/blob/master/lib/events.js
// Regular expression used to split event strings
var eventSplitter = /\s+/;
// A module that can be mixed in to *any object* in order to provide it
// with custom events. You may bind with `on` or remove with `off` callback
// functions to an event; `trigger`-ing an event fires all callbacks in
// succession.
//
//     var object = new Events();
//     object.on('expand', function(){ alert('expanded'); });
//     object.trigger('expand');
//
function Events() {
}

// Bind one or more space separated events, `events`, to a `callback`
// function. Passing `"all"` will bind the callback to all events fired.
Events.prototype.on = function (events, callback, context) {
    var cache, event, list;
    if (!callback) return this;
    cache = this.__events || (this.__events = {});
    events = events.split(eventSplitter);
    while (event = events.shift()) {
        list = cache[event] || (cache[event] = []);
        list.push(callback, context);
    }
    return this;
};
// Remove one or many callbacks. If `context` is null, removes all callbacks
// with that function. If `callback` is null, removes all callbacks for the
// event. If `events` is null, removes all bound callbacks for all events.
Events.prototype.off = function (events, callback) {
    var cache, event, list, i;
    // No events, or removing *all* events.
    if (!(cache = this.__events)) return this;
    if (!(events || callback)) {
        delete this.__events;
        return this;
    }
    events = events ? events.split(eventSplitter) : keys(cache);
    // Loop through the callback list, splicing where appropriate.
    while (event = events.shift()) {
        list = cache[event];
        if (!list) continue;
        if (!(callback)) {
            delete cache[event];
            continue;
        }
        for (i = list.length - 2; i >= 0; i -= 2) {
            if (!(callback && list[i] !== callback)) {
                list.splice(i, 2);
            }
        }
    }
    return this;
};
// Trigger one or many events, firing all bound callbacks. Callbacks are
// passed the same arguments as `trigger` is, apart from the event name
// receive the true name of the event as the first argument).
Events.prototype.trigger = function (events) {
    var cache, event, list, i, len, rest = [];
    if (!(cache = this.__events)) return this;
    events = events.split(eventSplitter);
    // Fill up `rest` with the callback arguments.  Since we're only copying
    // the tail of `arguments`, a loop is much faster than Array#slice.
    for (i = 1, len = arguments.length; i < len; i++) {
        rest[i - 1] = arguments[i];
    }
    // For each event, walk through the list of callbacks twice, first to
    while (event = events.shift()) {
        // Copy callback lists to prevent modification.
        if (list = cache[event]) list = list.slice();
        // Execute event callbacks.
        callEach(list, rest, this);
    }
};
// Mix `Events` to object instance or Class function.
Events.mixTo = function (receiver) {
    receiver = receiver.prototype || receiver;
    var proto = Events.prototype;
    for (var p in proto) {
        if (proto.hasOwnProperty(p)) {
            receiver[p] = proto[p];
        }
    }
};
// Helpers
// -------
var keys = Object.keys;
if (!keys) {
    keys = function (o) {
        var result = [];
        for (var name in o) {
            if (o.hasOwnProperty(name)) {
                result.push(name);
            }
        }
        return result;
    };
}

// Execute callbacks
function callEach(list, args, context) {
    if (list) {
        for (var i = 0, len = list.length; i < len; i += 1) {
            list[i].apply(context, args);
        }
    }
}
